package com.vhall.component.service.vote.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.vhall.component.dao.vote.RoomVoteLkMapper;
import com.vhall.component.dao.vote.VoteAnswersMapper;
import com.vhall.component.dao.vote.VotesMapper;
import com.vhall.component.entity.vote.RoomVoteLkEntity;
import com.vhall.component.entity.vote.VoteAnswersEntity;
import com.vhall.component.entity.vote.VotesEntity;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.common.utils.RandomUtil;
import com.vhall.component.plugin.common.utils.RedisLocker;
import com.vhall.component.service.vote.VoteCacheService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author yuanzh
 */
@Slf4j

@Service
public class VoteCacheServiceImpl implements VoteCacheService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RoomVoteLkMapper roomVoteLkMapper;

    @Autowired
    private VoteAnswersMapper voteAnswersMapper;

    @Autowired
    private VotesMapper votesMapper;

    private static final String INTERRUPTED_MESSAGE = "Interrupted!";

    @Override
    public RoomVoteLkEntity getRoomVoteLk(String roomId, Long voteId) {
        String cacheKey = RedisKey.ROOM_VOTE_LK_DATA_CACHE_PRE_KEY + roomId;
        BoundHashOperations hashOps = redisTemplate.boundHashOps(cacheKey);
        String voteIdStr = String.valueOf(voteId);
        Object cachedObj = hashOps.get(voteIdStr);
        if (Objects.isNull(cachedObj)) {
            String lockKey = cacheKey + ":" + voteId;
            if (!RedisLocker.tryLock(lockKey, "1", 10 * 1000L)) {
                try {
                    Thread.sleep(50L);
                } catch (InterruptedException e) {
                    log.error(INTERRUPTED_MESSAGE, e);
                    Thread.currentThread().interrupt();
                }
                return getRoomVoteLk(roomId, voteId);
            }
            try {
                RoomVoteLkEntity entity = roomVoteLkMapper.selectOne(
                        Wrappers.<RoomVoteLkEntity>lambdaQuery().eq(RoomVoteLkEntity::getVoteId, voteId)
                                .eq(RoomVoteLkEntity::getRoomId, roomId));
                if (Objects.nonNull(entity)) {
                    hashOps.put(voteIdStr, JsonUtil.toJsonString(entity));
                    // 缓存3~5秒即可
                    hashOps.expire(RandomUtil.getRandomNum(3, 5), TimeUnit.SECONDS);
                }
                return entity;
            } finally {
                RedisLocker.unlock(lockKey, "1");
            }
        }
        return JsonUtil.objectFromJSONString(cachedObj.toString(), RoomVoteLkEntity.class);
    }

    @Override
    public VoteAnswersEntity getVoteAnswer(Long answerId) {
        String cacheKey = RedisKey.VOTE_ANSWERS_DATA_CACHE_PRE_KEY + answerId;
        BoundValueOperations ops = stringRedisTemplate.boundValueOps(cacheKey);
        Object cachedObj = ops.get();
        if (Objects.isNull(cachedObj)) {
            if (!RedisLocker.tryLock(cacheKey, "1", 10 * 1000L)) {
                try {
                    Thread.sleep(50L);
                } catch (InterruptedException e) {
                    log.error(INTERRUPTED_MESSAGE, e);
                    Thread.currentThread().interrupt();
                }
                return getVoteAnswer(answerId);
            }
            try {
                VoteAnswersEntity entity = voteAnswersMapper.selectById(answerId);
                if (Objects.nonNull(entity)) {
                    ops.set(JsonUtil.toJsonString(entity));
                    // 缓存3~5秒即可
                    ops.expire(RandomUtil.getRandomNum(3, 5), TimeUnit.SECONDS);
                }
                return entity;
            } finally {
                RedisLocker.unlock(cacheKey, "1");
            }
        }
        return JsonUtil.objectFromJSONString(cachedObj.toString(), VoteAnswersEntity.class);
    }

    @Override
    public VotesEntity getVote(Long voteId) {
        String cacheKey = RedisKey.VOTE_DATA_CACHE_PRE_KEY + voteId;
        BoundValueOperations ops = stringRedisTemplate.boundValueOps(cacheKey);
        Object cachedObj = ops.get();
        if (Objects.isNull(cachedObj)) {
            if (!RedisLocker.tryLock(cacheKey, "1", 10 * 1000L)) {
                try {
                    Thread.sleep(50L);
                } catch (InterruptedException e) {
                    log.error(INTERRUPTED_MESSAGE, e);
                    Thread.currentThread().interrupt();
                }
                return getVote(voteId);
            }
            try {
                VotesEntity entity = votesMapper.selectById(voteId);
                if (Objects.nonNull(entity)) {
                    ops.set(JsonUtil.toJsonString(entity));
                    // 缓存3~5秒即可
                    ops.expire(RandomUtil.getRandomNum(3, 5), TimeUnit.SECONDS);
                }
                return entity;
            } finally {
                RedisLocker.unlock(cacheKey, "1");
            }
        }
        return JsonUtil.objectFromJSONString(cachedObj.toString(), VotesEntity.class);
    }
}
